/**
 * Title: Hyperbolic engine
 * Description: jFamilyTree Engine
 * Version: provider
 * Copyright: (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 * Update: Mon Mar 10 00:00:00 CEST 2008
 * Algorithm:	Vladmir Bulatov
 */

package jFamilyTree.core;

/**
 * Arc of radius r with center at x,y beginning 'start' and spanning 'angle' angles in radians
 * 
 * @author Bernard Bou
 */
public class Arc
{
	// constants

	/**
	 * epsilon
	 */
	static private final double E = 1.e-4;

	/**
	 * max radius
	 */
	static private final double R = 30.;

	// data

	/**
	 * center x
	 */
	public double x;

	/**
	 * center y
	 */
	public double y;

	/**
	 * radius
	 */
	public double r;

	/**
	 * start
	 */
	public double start;

	/**
	 * angle
	 */
	public double angle;

	/**
	 * from-end
	 */
	public Complex from;

	/**
	 * to-end
	 */
	public Complex to;

	// C O N S T R U C T O R

	/**
	 * Construct segment of hyperbolic circle, which connects z1 and z2
	 * 
	 * @param z1
	 *        start endpoint
	 * @param z2
	 *        end enpoint
	 */
	public Arc(Complex z1, Complex z2)
	{
		// ends
		from = z1;
		to = z2;

		// z1, z2, enter are aligned -> draw line
		if (intersectOrigin())
		{
			r = 0.;
			return;
		}

		// center
		double s1 = 1. + z1.abs2();
		double s2 = 1. + z2.abs2();
		double norm = 1. / (2 * (z1.re * z2.im - z2.re * z1.im));
		Complex thisCenter = new Complex((s1 * z2.im - s2 * z1.im) * norm, -(s1 * z2.re - s2 * z1.re) * norm);

		// radius
		double thisRadius = EuclidianLocation.getDistance(thisCenter, z2);
		if (thisRadius > R)
		{
			r = 0.;
			return;
		}

		// start angle
		Complex t1 = new Complex(z1).sub(thisCenter);
		double thisStartAngle = t1.arg(); // Arg(z1-thisCenter);

		// normalize
		if (thisStartAngle < 0.)
			thisStartAngle += 2. * Math.PI;

		// end angle
		Complex t2 = new Complex(z2).sub(thisCenter);
		double thisAngleEnd = t2.arg(); // Arg(z2-thisCenter);

		// normalize
		if (thisAngleEnd < 0.)
			thisAngleEnd += 2. * Math.PI;

		// angle extent
		double thisAngleExtent = thisAngleEnd - thisStartAngle;

		// normalize
		if (thisAngleExtent > Math.PI)
			thisAngleExtent -= 2. * Math.PI;
		else if (thisAngleExtent < -Math.PI)
			thisAngleExtent += 2. * Math.PI;

		// result
		x = thisCenter.re;
		y = thisCenter.im;
		r = thisRadius;
		start = thisStartAngle;
		angle = thisAngleExtent;
	}

	/**
	 * Compute if this arc meets origin
	 * 
	 * @return true if this arc meets origin
	 */
	private boolean intersectOrigin()
	{
		double fromAbs = from.abs2();
		double fromArg = from.arg();
		double toAbs = to.abs2();
		double toArg = to.arg();
		return ((fromAbs < E || toAbs < E) || ((fromAbs >= E && toAbs >= E) && ((Math.abs(fromArg - toArg) < E || (Math.abs(fromArg - toArg - Math.PI) < E) || (Math.abs(fromArg - toArg + Math.PI) < E)))));
	}
}
